package com.office.office.oo;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import com.office.config.BuilderConfigInfo;
import com.office.config.oo.FileConfig;
import com.office.config.oo.OnlyProperties;
import com.office.config.oo.Plugins;
import com.office.config.oo.document.DocumentPermission;
import com.office.config.oo.edit.FileCustomization;
import com.office.config.oo.edit.FileEmbedded;
import com.office.config.oo.edit.FileUser;
import com.office.core.*;
import com.office.core.context.FileMetadata;
import com.office.tools.SecurityUtils;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/*
 * @BelongsProject: office
 * @BelongsPackage: com.office.office.only
 * @Author: TongHui
 * @CreateTime: 2024-03-13 11:01
 * @Description: TODO
 * @Version: 1.0
 */
@Data
public abstract class AbstractOOBase{

    private static final Logger log = LoggerFactory.getLogger(AbstractOOBase.class);


    protected final CommonConfig configuration;
    protected Cache cache;
    protected SaveFileProcessor saveFileProcessor;


    /**
     * 配置信息 ============================
     */
    protected OnlyProperties onlyProperties ;
    protected DocumentPermission viewPermission ;
    protected DocumentPermission editPermission ;
    protected FileCustomization customization ;
    protected FileEmbedded embedded ;
    protected Plugins plugins ;
    protected JSONObject convert ;
    /**
     *  ============================
     */

    protected String EDIT = "edit";
    protected String VIEW = "view";

    public AbstractOOBase(CommonConfig configuration) {
        this.configuration = configuration;
    }

    /**
     * @return 服务名称
     */
    protected String getServerName() {
        return "onlyOffice-";
    }
    /**
     * 缓存数据
     * @param key   键
     * @param value 值
     */
    public void setCache(String key, String value) {
        cache.set(key, value);
    }

    /**
     * 获取规定历史文件数量
     * @return 历史文件数量
     */
    public Integer getHistNum() {
        return configuration.getHistNum();
    }

    /**
     * 保存文件回调地址
     * @return 地址
     */
    public String getCommandServiceUrl() {
        return onlyProperties.getDocService() + onlyProperties.SAVE;
    }


    /**
     * 文件下载地址
     * @param fineId 文件id
     * @return 下载地址
     */
    public String getDowloadUrl(String fineId) {
        if (configuration.getDowloadFile().endsWith("/")) {
            return configuration.getDowloadFile() + fineId ;
        }
        return configuration.getDowloadFile() + "/"+ fineId;
    }

    /**
     *  获取打开文档时的唯一key
     * @param fileId 文件id
     * @return key
     */
    public String getKey(String fileId) {
        FileUser user = (FileUser) SecurityUtils.getUserSession();
        if (cache.hasKey(user.getId() + "_" + fileId)) {
            return (String) cache.get(user.getId() + "_" + fileId);
        }
        return (String) cache.get("collaborativeEditing_" + fileId);
    }

    /**
     * 缓存中存放文件使用人数
     * @param key    文件唯一标识
     * @param users  用户人数
     * @return 当前人数
     */
    public int iskey(String key, Integer users) {
        int i = 0;
        if (users != null) {
            cache.set(key, users);
            i = users;
        } else {
            i = ((int) cache.get(key)) - 1;
            if (i<=0){
                cache.remove(key);
            }else {
                cache.set(key, i);
            }
        }
        log.info("[" + key + "]文档使用人数：" + i);
        return i;
    }

    /**
     * 获取文件的当前使用人数
     * @param key   文件唯一标识
     * @return 当前人数
     */
    public int getUserNum(String key) {
        return (int) (cache.get(key));
    }

    /**
     *
     * @param key  文件唯一标识
     * @return 文件id
     */
    public String getFileId(String key) {
        return (String) cache.get("getID_" + key);
    }

    /**
     * 临时文件信息
     *
     * @param key  文件唯一标识
     * @return 文件元数据
     */
    public FileMetadata getTempFile(String key) {
        if (cache.hasKey(getServerName() + key)) {
            FileMetadata tempFileInfo = (FileMetadata) cache.get(getServerName() + key);
            return tempFileInfo;
        }
        return null;
    }


    /**
     * 删除临时文件信息
     * @param jsonObject  oo服务器回调参数
     */
    public void removeTempFile(JSONObject jsonObject) {
        String key = (String) jsonObject.get("key");
        if (cache.hasKey(getServerName() + key)) {
            FileMetadata tempFileInfo = (FileMetadata) cache.get(getServerName() + key);
            log.info("临时文件信息缓存:key:{},data:{}",key,tempFileInfo);
            cache.remove(getServerName() + key);
        }
    }


    /**
     * @param map  文件信息{
     *           必填 fileId
     *           必填 fileName
     *           必填 fileType
     *           必填 fileSize
     *           可用携带其它值
     *        }
     * @param collaborativeEditing  是否协同
     * @return 文件元数据
     * @throws Exception 返回异常
     */
    protected FileMetadata handlerFile(Map<String, Object> map, Boolean collaborativeEditing) throws Exception {

        try {
            String id = (String) map.get("fileId");

            log.info("源文件存储的ID" + id);
            // 唯一标示符
            /**
             *  协同编辑时用的同一个key，判断是否协同，是：查找缓存中有无key，否：新生成key
             * */
            String key = IdUtil.simpleUUID(); //默认 查看模式
            String mode = (String) map.get("mode");
            if (mode.equals("edit")) { //编辑模式
                if (collaborativeEditing) {
                    if (cache.get("collaborativeEditing_" + id) != null) {
                        key = (String) cache.get("collaborativeEditing_" + id);
                    } else {
                        cache.set("collaborativeEditing_" + id, key);
                        cache.set("getID_" + key, id);
                    }
                } else {
                    FileUser user = (FileUser) SecurityUtils.getUserSession();
                    cache.set(user.getId() + "_" + id, key);
                    cache.set("getID_" + key, id);
                }
            }


            if (cache.hasKey(getServerName() + key)) {
                FileMetadata fileMetadata = (FileMetadata) cache.get(getServerName() + key);
                fileMetadata.setOpenTime(new Date().getTime());
                cache.set(getServerName() + key,fileMetadata);
                return fileMetadata;
            }


            //  生成临时可访问文件的url
            String tempUrl = getDowloadUrl(id);

            FileMetadata tempFileInfo = FileMetadata.builder()
                    // 可访问路径
                    .url(tempUrl)
                    // 原来的文件名
                    .oldName((String) map.get("fileName"))
                    .fileType((String) map.get("fileType"))
                    .fileInfo(map)
                    // 唯一标识
                    .key(key)
                    .openTime(new Date().getTime())
                    .build();
            if (mode.equals(EDIT)) {
                cache.set(getServerName()+key,tempFileInfo,Cache.TIMEOUT_DAY);
            }
            return tempFileInfo;
        } catch (Exception e) {
            log.error("生成临时文件失败", e);
            throw new Exception(e.getMessage());
        }
    }


    /**
     * @param map   文件信息
     * @param mode  模式
     * @param collaborativeEditing  是否协同
     * @param documentPermission    权限信息
     * @return   配置信息
     */
    protected FileConfig openEditConfig(Map<String, Object> map, String mode, boolean collaborativeEditing,DocumentPermission documentPermission) {
        try {
            map.put("mode", mode);
            log.info("开始生成文件信息");
            //在编辑模式 生成临时文件，保存原文件信息
            FileMetadata tempFileInfo = handlerFile(map, collaborativeEditing);
            //生成配置文件 TODO: 控制文件权限
            log.info("开始生成编辑器配置信息");


            FileConfig fileConfigDTO = BuilderConfigInfo.buildOnlyConfig(
                    tempFileInfo.getUrl(),
                    mode,
                    tempFileInfo.getKey(),
                    tempFileInfo.getOldName(),
                    configuration.getLocalhostAddress() + onlyProperties.getCallBackUrl(),
                    onlyProperties,
                    (FileUser) SecurityUtils.getUserSession(),
                    plugins,
                    getPermission(mode,documentPermission),
                    customization);
            log.info("生成编辑器配置信息结束");
            // TODO: 添加更多详细的自定义信息
            return fileConfigDTO;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 关闭文档
     * @param jsonObject  oo服务回调参数
     */
    public abstract void close(JSONObject jsonObject);



    /**
     * @param map                  文件信息
     * @param map{                 必填 fileId
     *                             必填 fileName
     *                             必填 fileType
     *                             必填 fileSize
     *                             可用携带其它信息
     *                             }
     * @param mode                 打开方式 edit/view
     * @param collaborativeEditing 是否协同编辑
     * @param documentPermission   权限信息 非必填
     * @return
     */
    public abstract Map openDocument(Map<String, Object> map, String mode, boolean collaborativeEditing,DocumentPermission documentPermission) ;

    /**
     *  文件保存 状态为6时调用
     * @param jsonObject
     * @throws Exception
     */
    public abstract void handlerStatus(JSONObject jsonObject) throws Exception ;

    /**
     * 手动执行保存回调
     * @param key     文件唯一标识
     * @param userId   调用接口的用户
     * @return  消息
     */
    public abstract String save(String key, String userId) ;
    /**
     * 文件转化 #简单请求参数示例:
     * @param filetype   文件类型
     * @param key        文件key
     * @param outputtype 输出类型
     * @param title      输出文件名称
     * @param password   加密文档的密码
     * @return 文件路径
     */
    public abstract String converted(String filetype, String key, String outputtype, String title, String password);

    protected DocumentPermission getPermission(String mode,DocumentPermission documentPermission){
        if (documentPermission != null){
            return documentPermission;
        }
        return EDIT.equals(mode)?editPermission:viewPermission;
    }

    /*
     * 错误代码	描述
     * -1	未知错误。
     * -2	转换超时错误。
     * -3	转换错误。
     * -4	下载要转换的文档文件时出错。
     * -5	密码不正确。
     * -6	访问转换结果数据库时出错。
     * -7	输入错误。
     * -8	令牌无效。
     */
    protected void ProcessConvertServiceResponceError(int errorCode) throws Exception {
        String errorMessage = "";
        String errorMessageTemplate = "Error occurred in the ConvertService: ";
        switch (errorCode) {
            case -8:
                errorMessage = errorMessageTemplate + "令牌无效";
                break;
            case -7:
                errorMessage = errorMessageTemplate + "输入错误";
                break;
            case -6:
                errorMessage = errorMessageTemplate + "访问转换结果数据库时出错";
                break;
            case -5:
                errorMessage = errorMessageTemplate + "密码不正确";
                break;
            case -4:
                errorMessage = errorMessageTemplate + "下载要转换的文档文件时出错";
                break;
            case -3:
                errorMessage = errorMessageTemplate + "转换错误";
                break;
            case -2:
                errorMessage = errorMessageTemplate + "转换超时错误";
                break;
            case -1:
                errorMessage = errorMessageTemplate + "未知错误";
                break;
            case 0:
                break;
            default:
                errorMessage = "ErrorCode = " + errorCode;
                break;
        }
        throw new Exception(errorMessage);
    }

    protected String saveMessage(Integer error){
        String msg = "";
        switch ((Integer) error) {
            case 1:
                msg = "缺少文档密钥或找不到具有此类密钥的文档";
                break;
            case 2:
                msg = "回调网址不正确";
                break;
            case 3:
                msg = "内部服务器错误";
                break;
            case 4:
                msg = "在收到强制保存命令之前，未对文档应用任何更改";
                break;
            case 5:
                msg = "命令不正确";
                break;
            case 6:
                msg = "令牌无效";
                break;
            default:
                msg = "保存成功";
        }
        return msg;
    }
}
